/*
 * tmr.c
 *
 * Created: 6/13/2013 3:28:03 PM
 *  Author: Tim
 */ 

//use to gain access to Xmega registers
#include <avr/io.h>
#include <avr/interrupt.h>
#include <math.h>

#include "tmr.h"

#include "../../packets/handlers/adcPacketHandler.h"
#include "../../packets/handlers/dacPacketHandler.h"
#include "../../packets/handlers/dioPacketHandler.h"
#include "../../packets/handlers/encPacketHandler.h"
#include "../dio/dio.h"


tmrAlloc_t tmrCheckFrequency(uint16_t freq)
{
	uint32_t clockCycles = round(F_CPU/freq);
	uint8_t  division;
			
	if		(clockCycles > UINT16_MAX * 1024)
	{
		clockCycles = round(clockCycles/1024);
		division = TC_CLKSEL_DIV1024_gc;
	}
	else if (clockCycles > UINT16_MAX * 256)
	{
		clockCycles = round(clockCycles/256);
		division = TC_CLKSEL_DIV256_gc;
	}
	else if (clockCycles > UINT16_MAX * 64)
	{
		clockCycles = round(clockCycles/64);
		division = TC_CLKSEL_DIV64_gc;
	}
	else if (clockCycles > UINT16_MAX * 8)
	{
		clockCycles = round(clockCycles/8);
		division = TC_CLKSEL_DIV8_gc;
	}
	else if (clockCycles > UINT16_MAX * 4)
	{
		clockCycles = round(clockCycles/4);
		division = TC_CLKSEL_DIV4_gc;
	}
	else if (clockCycles > UINT16_MAX * 2)
	{
		clockCycles = round(clockCycles/2);
		division = TC_CLKSEL_DIV2_gc;
	}
	else
	{
		division = TC_CLKSEL_DIV1_gc;
	}
			
	//check for timers with the same frequency already setup
			
	if		( (TCC0.PER == clockCycles) && TCC0.CTRLA == division)	return TIMER_C0;
	else if	( (TCC1.PER == clockCycles) && TCC1.CTRLA == division)	return TIMER_C1;
	else if	( (TCD0.PER == clockCycles) && TCD0.CTRLA == division)	return TIMER_D0;
	else if	( (TCD1.PER == clockCycles) && TCD1.CTRLA == division)	return TIMER_D1;
	else if	( (TCE0.PER == clockCycles) && TCE0.CTRLA == division)	return TIMER_E0;
	else return TIMER_NULL;
}


//returns the next free timer in line
tmrAlloc_t tmrCheckAvailability()
{		
	
	if		(daqAlloc.timerAlloc.timerC0Alloc == FEATURE_FREE)		return	TIMER_C0;
	else if (daqAlloc.timerAlloc.timerC1Alloc == FEATURE_FREE)		return	TIMER_C1;
	else if (daqAlloc.timerAlloc.timerD0Alloc == FEATURE_FREE)		return	TIMER_D0;
	else if (daqAlloc.timerAlloc.timerD1Alloc == FEATURE_FREE)		return	TIMER_D1;
	else if (daqAlloc.timerAlloc.timerE0Alloc == FEATURE_FREE)		return	TIMER_E0;
	else															return	TIMER_NULL;
}

//returns the number of available timers
uint8_t tmrNumAvailabile()
{
	uint8_t numberOfFreeTimers = 0;
	
	if (daqAlloc.timerAlloc.timerC0Alloc == FEATURE_FREE)	numberOfFreeTimers++;
	if (daqAlloc.timerAlloc.timerC1Alloc == FEATURE_FREE)	numberOfFreeTimers++;
	if (daqAlloc.timerAlloc.timerD0Alloc == FEATURE_FREE)	numberOfFreeTimers++;
	if (daqAlloc.timerAlloc.timerD1Alloc == FEATURE_FREE)	numberOfFreeTimers++;
	if (daqAlloc.timerAlloc.timerE0Alloc == FEATURE_FREE)	numberOfFreeTimers++;
	
	return numberOfFreeTimers;
}

//sets a timer interrupt to occurs at a specified frequency
status_t tmrSetupFreq(tmrAlloc_t tmrMask, uint32_t freq)
{
	
	uint32_t clockCycles = round(F_CPU/freq);
	uint8_t  division;
	
	if		(clockCycles > UINT16_MAX * 1024)	
	{
		clockCycles = round(clockCycles/1024);
		division = TC_CLKSEL_DIV1024_gc;	
	}
	else if (clockCycles > UINT16_MAX * 256)	
	{
		clockCycles = round(clockCycles/256);
		division = TC_CLKSEL_DIV256_gc;	
	}
	else if (clockCycles > UINT16_MAX * 64)		
	{
		clockCycles = round(clockCycles/64);
		division = TC_CLKSEL_DIV64_gc;	
	}
	else if (clockCycles > UINT16_MAX * 8)		
	{
		clockCycles = round(clockCycles/8);
		division = TC_CLKSEL_DIV8_gc;	
	}
	else if (clockCycles > UINT16_MAX * 4)
	{
		clockCycles = round(clockCycles/4);
		division = TC_CLKSEL_DIV4_gc;	
	}
	else if (clockCycles > UINT16_MAX * 2)		
	{
		clockCycles = round(clockCycles/2);
		division = TC_CLKSEL_DIV2_gc;	
	}
	else
	{
		division = TC_CLKSEL_DIV1_gc;			
	}
	
	switch(tmrMask)
	{

		case TIMER_C0:

			TCC0.CTRLA = division;
			TCC0.PER   = clockCycles;
			daqAlloc.timerAlloc.timerC0Alloc = FEATURE_UTILIZED;
			return OPP_SUCCESSFUL;
			
		break;	
		case TIMER_C1:
		
			TCC1.CTRLA = division;
			TCC1.PER   = clockCycles;
			daqAlloc.timerAlloc.timerC1Alloc = FEATURE_UTILIZED;
			return OPP_SUCCESSFUL;
			
		break;	
		case TIMER_D0:
		
			TCD0.CTRLA = division;
			TCD0.PER   = clockCycles;
			daqAlloc.timerAlloc.timerD0Alloc = FEATURE_UTILIZED;
			return OPP_SUCCESSFUL;
		
		break;	
		case TIMER_D1:
		
			TCD1.CTRLA = division;
			TCD1.PER   = clockCycles;
			daqAlloc.timerAlloc.timerD1Alloc = FEATURE_UTILIZED;
			return OPP_SUCCESSFUL;
			
		break;	
		case TIMER_E0:
		
			TCE0.CTRLA = division;
			TCE0.PER   = clockCycles;
			daqAlloc.timerAlloc.timerE0Alloc = FEATURE_UTILIZED;
			return OPP_SUCCESSFUL;
		
		break;
		case TIMER_NULL:
		
			return OPP_FAILED;
		
		break;			
	}
}

void timerInterruptHandler(TC0_t *tmr,tmrAlloc_t selTmr)
{	
	//turn off data register empty interrupt while in timer interrupt
	//this will just mean data will accumulate int he tx buffer
	USARTD1.CTRLA &= ~USART_DREINTLVL_gm;

		
	//enable DRE to send any data that may have accumulated in the TX buf
	//if there is nothing there no worries because the interrupt will disable itself
	USARTD1.CTRLA |= USART_DREINTLVL_LO_gc;		
}


ISR(TCC0_OVF_vect)
{
	timerInterruptHandler(&TCC0,TIMER_C0);
}

ISR(TCC1_OVF_vect)
{
	timerInterruptHandler(&TCC1,TIMER_C1);
}

ISR(TCD0_OVF_vect)
{
	timerInterruptHandler(&TCD0,TIMER_D0);
}

ISR(TCD1_OVF_vect)
{
	timerInterruptHandler(&TCD1,TIMER_D1);
}

ISR(TCE0_OVF_vect)
{
	timerInterruptHandler(&TCE0,TIMER_E0);
}
